// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <nebula/json/object_parser.h>
#include <nebula/json/rapidjson_defs.h>  // IWYU pragma: keep

#include <merak/json.h>

namespace nebula::json::internal {

    class ObjectParser::Impl {
    public:
        turbo::Status parse(std::string_view json) {
            document_.parse(reinterpret_cast<const merak::json::Document::char_type *>(json.data()),
                            static_cast<size_t>(json.size()));

            if (document_.has_parse_error()) {
                return turbo::invalid_argument_error("Json parse error (offset ", document_.get_error_offset(),
                                       "): ", document_.get_parse_error());
            }
            if (!document_.is_object()) {
                return turbo::failed_precondition_error("Not a json object");
            }
            return turbo::OkStatus();
        }

        turbo::Result<std::string> get_string(const char *key) const {
            if (!document_.has_member(key)) {
                return turbo::not_found_error("Key '", key, "' does not exist");
            }
            if (!document_[key].is_string()) {
                return turbo::failed_precondition_error("Key '", key, "' is not a string");
            }
            return document_[key].get_string();
        }

        turbo::Result<std::unordered_map<std::string, std::string>> get_string_map() const {
            std::unordered_map<std::string, std::string> map;
            for (auto itr = document_.member_begin(); itr != document_.member_end(); ++itr) {
                const auto &json_name = itr->name;
                const auto &json_value = itr->value;
                if (!json_name.is_string()) {
                    return turbo::failed_precondition_error("Key is not a string");
                }
                std::string name = json_name.get_string();
                if (!json_value.is_string()) {
                    return turbo::failed_precondition_error("Key '", name, "' does not have a string value");
                }
                std::string value = json_value.get_string();
                map.insert({std::move(name), std::move(value)});
            }
            return map;
        }

        turbo::Result<bool> get_bool(const char *key) const {
            if (!document_.has_member(key)) {
                return turbo::not_found_error("Key '", key, "' does not exist");
            }
            if (!document_[key].is_bool()) {
                return turbo::failed_precondition_error("Key '", key, "' is not a boolean");
            }
            return document_[key].get_bool();
        }

    private:
        merak::json::Document document_;
    };

    ObjectParser::ObjectParser() : impl_(new ObjectParser::Impl()) {}

    ObjectParser::~ObjectParser() = default;

    turbo::Status ObjectParser::parse(std::string_view json) { return impl_->parse(json); }

    turbo::Result<std::string> ObjectParser::get_string(const char *key) const {
        return impl_->get_string(key);
    }

    turbo::Result<bool> ObjectParser::get_bool(const char *key) const { return impl_->get_bool(key); }

    turbo::Result<std::unordered_map<std::string, std::string>> ObjectParser::get_string_map() const {
        return impl_->get_string_map();
    }

}  // namespace nebula::json::internal
